﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System;

namespace block_explorer
{
    class SpotLightRenderer
    {
        private Effect mLightEffect;
        private Model mModel;

        public SpotLightRenderer()
        {
        }

        public void Prepare(GraphicsDevice device, ContentManager content)
        {
            mLightEffect = content.Load<Effect>("Effects/SpotLight");
            mModel = content.Load<Model>("cone");
        }

        public void RenderLight(SpotLight light, SceneCamera camera, DeferredRenderer renderer)
        {
            mLightEffect.CurrentTechnique = mLightEffect.Techniques["Light"];

            float radAngle = MathHelper.ToRadians(light.Angle);
            float tan = (float)Math.Tan(radAngle);
            float cosSpotAngle = (float)Math.Cos(radAngle);
            float invRadiusSqr = 1.0f / (light.Range * light.Range);
            float angle = 1 - light.Angle / 90.0f; // for some reason a linear angle works better than the "proper" cos(angle)
            // sometimes? i dunno. what is going on?! maths!

            Matrix scaleMatrix = Matrix.CreateScale(light.Range * tan, light.Range * tan, light.Range);
            bool isUp = Math.Abs(light.Direction.Y) == 1.0f;
            Matrix lightWorld = Matrix.CreateWorld(light.Position, -light.Direction, isUp ? Vector3.Forward : Vector3.Up);

            mLightEffect.Parameters["World"].SetValue(scaleMatrix * lightWorld);
            mLightEffect.Parameters["View"].SetValue(camera.View);
            mLightEffect.Parameters["Projection"].SetValue(camera.Projection);
            mLightEffect.Parameters["InverseView"].SetValue(Matrix.Invert(camera.View));
            mLightEffect.Parameters["InverseProjection"].SetValue(Matrix.Invert(camera.Projection));

            mLightEffect.Parameters["LightView"].SetValue(light.Camera.View);
            mLightEffect.Parameters["LightProjection"].SetValue(light.Camera.Projection);
            mLightEffect.Parameters["InverseLightProjection"].SetValue(Matrix.Invert(light.Camera.Projection));

            mLightEffect.Parameters["CameraPosition"].SetValue(camera.Position);
            mLightEffect.Parameters["LightPosition"].SetValue(light.Position);
            mLightEffect.Parameters["LightDirection"].SetValue(light.Direction);
            mLightEffect.Parameters["LightColor"].SetValue(light.Color.ToVector3());

            mLightEffect.Parameters["HalfPixel"].SetValue(renderer.GBufferHalfPixel);

            mLightEffect.Parameters["InvLightRadiusSqr"].SetValue(invRadiusSqr);
            mLightEffect.Parameters["SpotAngle"].SetValue(cosSpotAngle);
            mLightEffect.Parameters["SpotExponent"].SetValue(light.Exponent / (1 - cosSpotAngle));

            mLightEffect.Parameters["Shadows"].SetValue(light.Shadows);

            mLightEffect.Parameters["NormalMap"].SetValue(renderer.GBuffer.Normal);
            mLightEffect.Parameters["DepthMap"].SetValue(renderer.GBuffer.Depth);
            mLightEffect.Parameters["ShadowMap"].SetValue(light.ShadowMap);

            RasterizerState rState = new RasterizerState();
            RasterizerState oldRState = renderer.GraphicsDevice.RasterizerState;
            rState.CullMode = CullMode.CullClockwiseFace;
            renderer.GraphicsDevice.RasterizerState = rState;

            foreach (ModelMesh mesh in mModel.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    mLightEffect.CurrentTechnique.Passes[0].Apply();
                    renderer.GraphicsDevice.SetVertexBuffer(part.VertexBuffer);
                    renderer.GraphicsDevice.Indices = part.IndexBuffer;
                    renderer.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
                }
            }

            renderer.GraphicsDevice.RasterizerState = oldRState;
        }
    }
}
